<?php
/**
 * Default model class for VO's
 *
 * @category    IPB_CMS
 * @package     App_Model
 * @copyright   Copyright (c) 2009 IPB CMS <http://ipb-cms.googlecode.com/>
 * @license     http://www.gnu.org/licenses/gpl-3.0.html GNU General Public License
 * @author      Henrique Moody <henriquemoody@gmail.com>
 */
abstract class App_Model_Abstract
{

    /**
     * Use as argument to return method result as object
     */
    const DAO_OBJECT = 'object';

    /**
     * Use as argument to return method result as string
     */
    const DAO_STRING = 'string';

    /**
     * Primery keys from the database that are present in the model
     *
     * @var array
     */
    private $_keys = array();

    /**
     * Name of the model
     *
     * @var string
     */
    protected $_modelName;

    /**
     * Contructor
     *
     * Sets the data of the model if the param is array
     * Sets the keys of the model if the param(s) i(are) an integer
     *
     * @return void
     */
    public function __construct()
    {
        $arguments = func_get_args();
        $keys      = array_keys($this->getKeys());
        foreach($arguments as $argument) {
            if (is_array($argument)) {
                $this->setData($argument);
            } elseif (is_int($argument)) {
                $name = current($keys);
                $method = $this->getMethodName($name, 'set');
                if (null === $method) {
                    continue;
                }
                $this->{$method}($argument);
                next($keys);
            }
        }
        $this->init();
    }

    /**
     * Fake constructor to be used in the model classes
     *
     * @return void
     */
    public function init()
    {
        // ...
    }

    /**
     * Return the model name without any prefix.
     *
     * @return string
     */
    public function getModelName()
    {
        if (null === $this->_modelName) {
            $fullname = get_class($this);
            $aPieces  = explode('_',$fullname);
            $lastname = array_pop($aPieces);
            $this->_modelName = $lastname;
        }
        return $this->_modelName;
    }

    /**
     * Returns the PRIMARY KEY(s) of the table
     *
     * @return array
     */
    public function getKeys()
    {
        $camelCaseToUnderscore = new Zend_Filter_Word_CamelCaseToUnderscore();
        $names = explode('_', $camelCaseToUnderscore->filter($this->getModelName()));
        foreach ($names as $name) {
            $keyName = strtolower($name) . 'Id';
            $method = $this->getMethodName($keyName, 'get');
            if (null === $method) {
                continue;
            }
            $this->_keys[$keyName] = $this->{$method}();
        }
        return $this->_keys;
    }

    /**
     * Returns the method name that exists for the given attribute
     * in the current object.
     * If NULL is return than there is no method for the attribute given.
     *
     * @param string $attribute
     * @param 'set'|'get'[optional] $prefix (Default: 'set')
     * @return string|null
     */
    public function getMethodName($attribute, $prefix = 'set')
    {
        $modelName  = $this->getModelName();
        $methodName = $prefix . str_replace($modelName , '', ucfirst($attribute));
        if (method_exists($this, $methodName)) {
            return $methodName;
        } elseif (($methodName = $prefix . ucfirst($attribute))
                && method_exists($this, $methodName)) {
            return $methodName;
        }
        return null;
    }

    /**
     * Returns the data based on the model keys
     *
     * @return array
     */
    public function getData()
    {
        $keys = $this->getKeys();
        $params = array();
        foreach($keys as $keyName => $keyValue) {
            $params[] = $keyValue;
        }
        $methodName = 'get' . $this->getModelName();
        $refMethod  = new ReflectionMethod($this->getDao(self::DAO_STRING), $methodName);
        $data       = $refMethod->invokeArgs($this->getDao(), $params);
        return $data;
    }

    /**
     * Sets the data of the current Model
     *
     * @param array $data
     * @return App_Model_Abstract Fluent interface, returns self
     */
    public function setData(array $data)
    {
        foreach ($data as $attr => $value) {
            $method = $this->getMethodName($attr, 'set');
            if (is_null($method)) {
                continue;
            }
            $this->{$method}($value);
        }
        return $this;
    }

    /**
     * Returns the DAO of the current VO
     *
     * @staticvar Zend_Db_Table_Abstract $instance
     * @param string $mode Flag: App_Model_Abstract::RETURN_OBJECT or App_Model_Abstract::RETURN_OBJECT
     * @return Zend_Db_Table_Abstract|string
     */
    public function getDao($mode = self::DAO_OBJECT)
    {
        static $instance;
        if (!$instance instanceof Zend_Db_Table_Abstract) {
            $name = 'Model_DbTable_' . $this->getModelName();
            if (!class_exists($name, true)) {
                throw new LogicException('DAO not found: ' . $name);
            }
            $instance = new $name();
        }
        return ($mode == self::DAO_OBJECT) ? $instance : get_class($instance);
    }

    /**
     * Retrieves information of the current Model from the database
     *
     * @return App_Model_Abstract Fluent interface, returns self
     */
    public function getInfo()
    {
        $data = $this->getData();
        $this->setData($data);
        return $this;
    }

}
